package spring.cloud.tasks.tasks_manager.tasks_manager.service;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheListener;

import java.util.*;

/**
 * 一个路径一个TreeCache,一个TreeCache绑定多个TreeCacheListener
 */
@Slf4j
@Setter
@Getter
public class TreeCacheToTreeCacheListenerListMap {
    private Map<String, TreeCache> pathToTreeCacheMap = new HashMap<String, TreeCache>();
    private Map<TreeCache, List<TreeCacheListener>> treeCacheToTreeCacheListenerListMap = new HashMap<TreeCache, List<TreeCacheListener>>();

    private String getPathAndDepthKey(String path, int depth) {
        return path + depth;
    }

    public TreeCache addTreeCacheIfAbsent(String path, int depth, TreeCache treeCache) {
        synchronized (this) {
            String pathAndDepthKey = getPathAndDepthKey(path, depth);
            if (!pathToTreeCacheMap.containsKey(pathAndDepthKey)) {
                pathToTreeCacheMap.put(pathAndDepthKey, treeCache);
                treeCacheToTreeCacheListenerListMap.put(treeCache, new ArrayList<TreeCacheListener>());
                return null;
            } else {
                return pathToTreeCacheMap.get(pathAndDepthKey);
            }
        }
    }

    public TreeCacheListener addTreeCacheListenerIfAbsent(String path, int depth, TreeCacheListener treeCacheListener) {
        synchronized (this) {
            TreeCacheListener oldTreeCacheListener = null;
            String pathAndDepthKey = getPathAndDepthKey(path, depth);
            TreeCache treeCache = pathToTreeCacheMap.get(pathAndDepthKey);
            if (treeCache == null) {
                log.error("The TreeCache is not exists, cannot add TreeCacheListener, path is {}, depth is {}", path, depth);
            } else {
                List<TreeCacheListener> treeCacheListenerList = treeCacheToTreeCacheListenerListMap.get(treeCache);
                boolean included = false;
                for (TreeCacheListener $treeCacheListener : treeCacheListenerList) {
                    Class<? extends TreeCacheListener> treeCacheListenerClass = treeCacheListener.getClass();
                    Class<? extends TreeCacheListener> $treeCacheListenerClass = $treeCacheListener.getClass();
                    if ($treeCacheListenerClass.equals(treeCacheListenerClass)) {
                        oldTreeCacheListener = $treeCacheListener;
                        included = true;
                        break;
                    }
                }
                if (included) {
                    log.info("The TreeCache has already included the instance of listener, will not be added, path is {}, depth is {}, listener is {}", path, depth, treeCacheListener.getClass());
                } else {
                    treeCacheListenerList.add(treeCacheListener);
                    treeCache.getListenable().addListener(treeCacheListener);
                }
            }
            return oldTreeCacheListener;
        }
    }

    public void removeTreeCache(String path, int depth) {
        String pathAndDepthKey = getPathAndDepthKey(path, depth);
        removeTreeCacheByKey(pathAndDepthKey);
    }

    public void removeTreeCacheByKey(String pathAndDepthKey) {
        synchronized (this) {
            TreeCache treeCache = pathToTreeCacheMap.get(pathAndDepthKey);
            if (treeCache != null) {
                treeCacheToTreeCacheListenerListMap.remove(treeCache);
                pathToTreeCacheMap.remove(pathAndDepthKey);
                treeCache.close();
                log.info("remove TreeCache success, path+depth is {}", pathAndDepthKey);
            }
        }
    }

    public boolean containsTreeCache(String path, int depth) {
        synchronized (this) {
            return pathToTreeCacheMap.containsKey(getPathAndDepthKey(path, depth));
        }
    }

    public void shutdown() {
        synchronized (this) {
            Iterator<Map.Entry<String, TreeCache>> iterator = pathToTreeCacheMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, TreeCache> next = iterator.next();
                try {
                    next.getValue().close();
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
                iterator.remove();
            }
            treeCacheToTreeCacheListenerListMap.clear();
        }
    }

}
